#include "Server.h"
#include "ServerUser.h"	

#include <Game/GameManager.h>

using namespace Network;

Server::Server()
:	CL_Service("CubeMUD_Server"), port("2345"), net_game(NULL), 
	next_user_id(1), game_running(false), gameMgr(NULL)
{
}

Server::~Server()
{
	service_stop();
	stop_event.set();
	thread.join();

	if(net_game)
		delete net_game;

	if(gameMgr)
		delete gameMgr;
}

void Server::service_start(std::vector<CL_String> &args)
{
	stop_event.reset();
	thread.start(this, &Server::exec);
}

void Server::service_stop()
{
	stop_event.set();
	thread.join();
}

void Server::exec()
{
	if(port == CL_String())
		return;

	net_game = new CL_NetGameServer();
	slots.connect(net_game->sig_client_connected(), this, &Server::onClientConnected);
	slots.connect(net_game->sig_client_disconnected(), this, &Server::onClientDisconnected);
	slots.connect(net_game->sig_event_received(), this, &Server::onEventReceived);

	gameMgr = new Game::GameManager(*this);

	login_events.func_event("Login").set(this, &Server::onEventLogin);

	net_game->start(port);
	cl_log_event("Network", "Server started");

	const unsigned int pulse_ms = 10;		//This should probably be configurable from some config script/txt-file/xml
	while(stop_event.wait(0) == 0)
	{
		CL_KeepAlive::process(pulse_ms);	//timeout every 10ms
		gameMgr->update(pulse_ms);			//Do a game world update step here
	}

	net_game->stop();
	cl_log_event("Network", "Server stopped");

	delete net_game;
	net_game = NULL;

	delete gameMgr;
	gameMgr = NULL;
}

void Server::onClientConnected(CL_NetGameConnection *connection)
{
	cl_log_event("Network", "Client connected");
	
	ServerUser *user = new ServerUser();
	user->attachToConnection(connection);
}

void Server::onClientDisconnected(CL_NetGameConnection *connection)
{
	cl_log_event("Network", "Client disconnected");

	ServerUser *user = ServerUser::getUser(connection);
	if(user)
		delete user;
}

void Server::onEventReceived(CL_NetGameConnection *connection, const CL_NetGameEvent &e)
{
	cl_log_event("Network", cl_format("Event received %1", e.get_name()));
	
	ServerUser *user = ServerUser::getUser(connection);
	if(user == NULL)
		return;

	bool handled_event = false;

	if (user->id == 0)	// User has not logged in, so route events to login dispatcher
		handled_event = login_events.dispatch(e, user);
	else
		handled_event = gameMgr->getUserGameEvents().dispatch(e, user);

	if (!handled_event)
		cl_log_event("events", "Unhandled event: %1", e.to_string());
}

void Server::onEventLogin(const CL_NetGameEvent &e, ServerUser *user)
{
	cl_log_event("Events", "Client requested login");

	CL_String user_name = e.get_argument(0);

	if(user_name.length() == 0)
	{
		user->sendEvent(CL_NetGameEvent("Login-Fail", "Missing user name"));
	}
	else
	{
		// Assign name and id to User object (created when user connected earlier)
		user->user_name = user_name;
		user->id = next_user_id++;

		user->sendEvent(CL_NetGameEvent("Login-Success"));
	}
}
